
#include "Base.h"
#include "Blackboard.h"

Base::Base(BWAPI::TilePosition loc, BWAPI::Unit* cc, std::list<BWAPI::Unit*>* incWorkers, bool isMyBase)	:	location(loc),
																												mainCommandCenter(cc),
																												needMoreWorkers(true),
																												isEnemyBase(!isMyBase),
																												LastCountOfBuildings(0),
																												toDelete(false)
{
	AddBuilding(cc);
	if(incWorkers)
	{
		unitLookup.insert(std::pair<BWAPI::UnitType,std::list<BWAPI::Unit*>>(BWAPI::UnitTypes::Terran_SCV,*incWorkers));
	}

	BuildLocalResourceList();
}

void Base::BuildLocalResourceList()
{
	MinPatch*	patch;
	Geyser*		vent;

	std::set<BWAPI::Unit*> min = BWAPI::Broodwar->getMinerals();
	std::set<BWAPI::Unit*> gas = BWAPI::Broodwar->getGeysers();

	BWAPI::Position ccPos = TileToPosition(location);

	for(std::set<BWAPI::Unit*>::iterator minIt = min.begin(), gasIt = gas.begin(); minIt != min.end(); minIt++)
	{
		if((*minIt)->isVisible())
		{
			if(isResourceInRange((*minIt),ccPos))
			{
				patch = new MinPatch();
				patch->mineral = *minIt;
				patch->mining = 0;
				patch->resourceGroupId = (*minIt)->getResourceGroup();
				minerals.push_back(patch);
			}
		}
		if(gasIt != gas.end())
		{
			if((*gasIt)->isVisible())
			{
				if(isResourceInRange((*gasIt),ccPos))
				{
					vent = new Geyser();
					vent->geyser = *gasIt;
					vent->refinery = NULL;
					vent->resourceGroupId = (*gasIt)->getResourceGroup();
					vent->gathering = 0;
					geysers.push_back(vent);
				}
			}
			gasIt++;
		}	
	}
}

bool Base::NeedMoreGas()
{
	//Basic implementation: Have we built as many refineries at this base as we can.  No?  Return true.  Yes? Return false.
	std::list<BWAPI::Unit*>* refineries = GetBuildingsByType(BWAPI::UnitTypes::Terran_Refinery);
	if(refineries)
		return false;
	return true;
	/*for(std::list<Geyser*>::iterator geyser = geysers.begin(); geyser != geysers.end(); geyser++)
	{
		if(!(*geyser)->refinery)
			return true;
	}
	return false;*/
}

void Base::Update()
{
	for(std::list<BWAPI::Unit*>::iterator it = buildingsTrainingUnits.begin(); it != buildingsTrainingUnits.end(); it++)
	{
	

	}

}


void Base::AddBuilding(BWAPI::Unit* building)
{
	std::map<BWAPI::UnitType,std::list<BWAPI::Unit*>>::iterator it = buildingLookup.find(building->getType());
	
	LastCountOfBuildings++;

	if(building->getType() == BWAPI::UnitTypes::Terran_Refinery)
	{
		for(std::list<Geyser*>::iterator it = geysers.begin(); it != geysers.end(); it++)
		{
			if(((*it)->gathering == 1)&&(!(*it)->refinery))
			{
				(*it)->refinery = building;
				break;
			}
		}
	}

	if(it == buildingLookup.end()) //New type of building
	{
		std::list<BWAPI::Unit*> buildings;
		buildings.push_back(building);
		buildingLookup.insert(std::pair<BWAPI::UnitType,std::list<BWAPI::Unit*>>(building->getType(),buildings));
	}
	else //Building type already in buildingLookup
	{
		it->second.push_back(building);
	}

	if(isEnemyBase)
	{
		std::map<BWAPI::UnitType,std::list<BWAPI::TilePosition>>::iterator it = enemyBuildingPositions.find(building->getType());
	
		if(it == enemyBuildingPositions.end()) //New type of building
		{
			std::list<BWAPI::TilePosition> buildings;
			buildings.push_back(building->getTilePosition());

			enemyBuildingPositions.insert(std::pair<BWAPI::UnitType,std::list<BWAPI::TilePosition>>(building->getType(),buildings));
		}
		else //Building type already in buildingLookup
		{
			std::list<BWAPI::TilePosition>::iterator pos = it->second.begin();
			for(; pos != it->second.end(); pos++)
			{
				if((*pos) == building->getTilePosition())
					break;
			}
			if(pos == it->second.end())
				it->second.push_back(building->getTilePosition());
		}


	}
}

void Base::AddUnit(BWAPI::Unit* unit)
{
	if(unit->getType().isBuilding())
	{
		AddBuilding(unit);
	}
	else
	{
		std::map<BWAPI::UnitType,std::list<BWAPI::Unit*>>::iterator it = unitLookup.find(unit->getType());
		if(it == unitLookup.end()) //New type of unit
		{
			std::list<BWAPI::Unit*> units;
			units.push_back(unit);
			unitLookup.insert(std::pair<BWAPI::UnitType,std::list<BWAPI::Unit*>>(unit->getType(),units));
		}
		else //Unit type already in unitLookup
		{
			it->second.push_back(unit);
			if(unit->getType() == BWAPI::UnitTypes::Terran_SCV && it->second.size() > 23)
				needMoreWorkers = false;
		}
	}
}
bool Base::RemoveBuilding(BWAPI::Unit* unit)
{
	std::map<BWAPI::UnitType,std::list<BWAPI::Unit*>>::iterator it = buildingLookup.find(unit->getType());
	if(it != buildingLookup.end()) //New type of unit
	{
		std::map<BWAPI::UnitType,std::list<BWAPI::Unit*>>::iterator it = buildingLookup.find(unit->getType());
		if(it != buildingLookup.end()) //New type of unit
		{
			for(std::list<BWAPI::Unit*>::iterator unitInList = it->second.begin(); unitInList != it->second.end(); unitInList++)
			{
				if((*unitInList) == unit)
				{
					it->second.erase(unitInList);
					return true;
				}
			}
		}
	}
	if(isEnemyBase)
	{
		std::map<BWAPI::UnitType,std::list<BWAPI::TilePosition>>::iterator it = enemyBuildingPositions.find(unit->getType());
	
		if(it != enemyBuildingPositions.end())
		{
			std::list<BWAPI::TilePosition>::iterator pos = it->second.begin();
			for(; pos != it->second.end(); pos++)
			{
				if((*pos) == unit->getTilePosition())
					break;
			}
			if(pos != it->second.end())
				it->second.erase(pos);
		}
	}
	if(buildingLookup.size() == 0)
		toDelete = true;
	return false;
}

bool Base::RemoveUnit(BWAPI::Unit* unit)
{
	if(unit->getType().isBuilding())
	{
		return RemoveBuilding(unit);
	}
	else
	{
		std::map<BWAPI::UnitType,std::list<BWAPI::Unit*>>::iterator it = unitLookup.find(unit->getType());
		if(it != unitLookup.end()) //New type of unit
		{
			for(std::list<BWAPI::Unit*>::iterator unitInList = it->second.begin(); unitInList != it->second.end(); unitInList++)
			{
				if((*unitInList) == unit)
				{
					it->second.erase(unitInList);
					return true;
				}
			}
		}
	}
	return false;
}

void Base::AddWorker(BWAPI::Unit* worker)
{

}


//Returns an array of two positions that define a 2d bounding box for this base
BWAPI::Position* Base::GetBaseBoundingBox()
{
	return NULL;
}

std::list<BWAPI::Unit*>* Base::GetDamagedBuildings()
{
	return NULL;
}

std::list<BWAPI::Unit*>* Base::GetIdleBuildings()
{
	return NULL;
}

std::list<BWAPI::Unit*>* Base::GetUnitsByType(BWAPI::UnitType type)
{
	if(type.isBuilding())
		return GetBuildingsByType(type);
	else
	{
		std::map<BWAPI::UnitType, std::list<BWAPI::Unit*>>::iterator it = unitLookup.find(type);
		if(it != unitLookup.end())	return &(it->second);
		return NULL;
	}
}

std::list<BWAPI::Unit*>* Base::GetBuildingsByType(BWAPI::UnitType type)
{
	std::map<BWAPI::UnitType, std::list<BWAPI::Unit*>>::iterator it = buildingLookup.find(type);
	if(it != buildingLookup.end())	return &(it->second);
	return NULL;
}

int Base::GetBuildingsCount()
{
	return buildingLookup.size();
}

int Base::GetLastCountOfBuildings()
{
	return LastCountOfBuildings;
}

void Base::DecLastCountOfBuildings()
{
	LastCountOfBuildings -= 3;
}

bool Base::isResourceInRange(BWAPI::Unit* res, BWAPI::Position &pos)
{
	BWAPI::Position resPos = res->getPosition();
	return (resPos.x() < pos.x() + RESOURCE_BOUND_X) && (resPos.x() > pos.x() - RESOURCE_BOUND_X) && (resPos.y() < pos.y() + RESOURCE_BOUND_Y) && (resPos.y() > pos.y() - RESOURCE_BOUND_Y);
}
